﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Etapa1.SofRender
{    
    /// <summary>
    /// Baricentric Rasterizer COM perspective correction nos Textcoords[n]
    /// </summary>
    public class BaricentricRasterizer
    {
        public BaricentricRasterizer()
        {
            
        }       

        public List<VertexType> DrawTriangle(Triangle tri)
        {
            List<VertexType> vertices = new List<VertexType>();

            Vector4 p0 = tri.P1[Slots.POSITION0];
            Vector4 p1 = tri.P2[Slots.POSITION0];
            Vector4 p2 = tri.P3[Slots.POSITION0];

            Vector4[] vecs = new Vector4[] { p0, p1, p2 };
            float minx = float.MaxValue;
            float miny = float.MaxValue;
            float maxx = float.MinValue;
            float maxy = float.MinValue;

            foreach (var item in vecs)
            {
                if (item.X < minx)
                {
                    minx = item.X;
                }
                if (item.X > maxx)
                {
                    maxx = item.X;
                }

                if (item.Y < miny)
                {
                    miny = item.Y;
                }
                if (item.Y > maxy)
                {
                    maxy = item.Y;
                }
            }

            for (int i = (int)minx - 1; i <= (int)maxx + 1; i++)
            {
                for (int j = (int)miny - 1; j <= (int)maxy + 1; j++)
                {
                    float alpha = 0;
                    float beta = 0;
                    float gama = 0;
                    ComputeAlphaBethaGama(ref alpha, ref beta, ref gama, tri.P1[Slots.POSITION0], tri.P2[Slots.POSITION0], tri.P3[Slots.POSITION0], i, j);

                    if (alpha >= 0 && alpha <= 1 && beta >= 0 && beta <= 1 && gama >= 0 && gama <= 1)
                    {
                        VertexType vt = new VertexType();                        
                        foreach (var item in tri.P1.VertexElements)
                        {
                            if (item == Slots.NORMAL0 || item == Slots.NORMAL1 || item == Slots.POSITION0 || item == Slots.POSITION1 || item == Slots.COLOR0 || item == Slots.COLOR1 )
                            {
                                vt[item] = alpha * tri.P1[item] + beta * tri.P2[item] + gama * tri.P3[item];                                
                            }
                            else
                            {

                                float w = (1f / tri.P1[Slots.POSITION0].W) * alpha
                                        + (1f / tri.P2[Slots.POSITION0].W) * beta
                                        + (1f / tri.P3[Slots.POSITION0].W) * gama;

                                float xx = (tri.P1[item].X / tri.P1[Slots.POSITION0].W) * alpha
                                    + (tri.P2[item].X / tri.P2[Slots.POSITION0].W) * beta
                                    + (tri.P3[item].X / tri.P3[Slots.POSITION0].W) * gama;

                                float yy = (tri.P1[item].Y / tri.P1[Slots.POSITION0].W) * alpha
                                    + (tri.P2[item].Y / tri.P2[Slots.POSITION0].W) * beta
                                    + (tri.P3[item].Y / tri.P3[Slots.POSITION0].W) * gama;

                                float zz = (tri.P1[item].Z / tri.P1[Slots.POSITION0].W) * alpha
                                    + (tri.P2[item].Z / tri.P2[Slots.POSITION0].W) * beta
                                    + (tri.P3[item].Z / tri.P3[Slots.POSITION0].W) * gama;

                                float ww = (tri.P1[item].W / tri.P1[Slots.POSITION0].W) * alpha
                                    + (tri.P2[item].W / tri.P2[Slots.POSITION0].W) * beta
                                    + (tri.P3[item].W / tri.P3[Slots.POSITION0].W) * gama;

                                float correctedx = xx / w;
                                float correctedy = yy / w;
                                float correctedz = zz / w;
                                float correctedw = ww / w;
                                vt[item] = new Vector4(correctedx, correctedy, correctedz, correctedw);
                            }
                        }
                        float z = alpha * p0.Z + beta * p1.Z + gama * p2.Z;                        
                        vt[Slots.POSITION0] = new Vector4(i, j, z, 1);                                                
                        vertices.Add(vt); 
                    }
                }                
            }
            return vertices;
        }

        void ComputeAlphaBethaGama(ref float alpha, ref float beta, ref float gama , Vector4 p0, Vector4 p1, Vector4 p2 , float x, float y)
        {
            alpha = ComputeFunction(x, y, p1, p2) / ComputeFunction(p0.X, p0.Y, p1, p2);
            beta = ComputeFunction(x, y, p2, p0)  / ComputeFunction(p1.X, p1.Y, p2, p0);
            gama = ComputeFunction(x, y, p0, p1)  / ComputeFunction(p2.X, p2.Y, p0, p1);
        }

        float ComputeFunction(float x, float y, Vector4 pa, Vector4 pb)
        {
            return (pa.Y - pb.Y) * x + (pb.X - pa.X) * y + pa.X * pb.Y - pb.X * pa.Y;
        }

    }
}
